<template>
  <div class="app-container">
    <div style="display: flex; flex-direction: row;">
      <div style="width: 65%; height: 100%; padding-right: 30px;">
        <el-row style="margin-bottom: 10px;">
          <el-col :span="24">
            <el-input
              v-model="searchPattern"
              placeholder="请输入key搜索..."
              class="search-input"
              @on-enter="searchKeys"
            />
            <el-input-number
              v-model="searchCount"
              placeholder="请输入搜索数量"
              style="width: 240px; margin-right: 5px;"
              @on-enter="searchKeys"
            />
            <el-button type="primary" @click="searchKeys">搜索</el-button>
            <el-tooltip content="清空缓存，谨慎操作" placement="bottom">
              <el-button type="danger" @click="deleteKeys">清空keys</el-button>
            </el-tooltip>
          </el-col>
        </el-row>
        <el-collapse accordion>
          <el-collapse-item>
            <template slot="title">数据库0 - (key总数：{{keysCount}})</template>
            <div style="height: 200px;">
              <el-scrollbar>
                <el-row>
                  <div v-for="item in keysList" :key="item.key">
                    <el-col :span="10" class="server-key-key" style="margin-bottom: 5px;">
                      <el-button
                        type="text"
                        size="small"
                        @click="getValueByKey(item.key)"
                      >{{item.key}}</el-button>
                    </el-col>
                    <el-col :span="2">
                      <el-tooltip content="删除缓存，谨慎操作" placement="top">
                        <el-button type="text" @click="deleteKey(item.key)">
                          <i class="el-icon-close" style="color: #F56C6C;" />
                        </el-button>
                      </el-tooltip>
                    </el-col>
                  </div>
                </el-row>
              </el-scrollbar>
            </div>
          </el-collapse-item>
        </el-collapse>
        <div style="margin-top: 20px;">
          <el-row class="server-value-title">
            <el-col :span="2">
              <strong class="server-value-key">Key:</strong>
            </el-col>
            <el-col :span="22">{{valueInfo.key}}</el-col>
          </el-row>
          <el-divider></el-divider>
          <el-row class="server-value-title">
            <el-col :span="2">
              <strong class="server-value-key">Size:</strong>
            </el-col>
            <el-col :span="22">{{valueInfo.size}}</el-col>
          </el-row>
          <el-divider></el-divider>
          <el-row class="server-value-title">
            <el-col :span="2">
              <strong class="server-value-key">TTL:</strong>
            </el-col>
            <el-col :span="22">{{valueInfo.expire}}{{valueInfo.expireFormat}}</el-col>
          </el-row>
          <el-divider></el-divider>
          <el-row class="server-value-value">
            <el-col :span="22">
              <strong class="server-value-key">Value</strong>
            </el-col>
            <el-col :span="2">
              <el-tooltip content="删除缓存，谨慎操作" placement="top">
                <el-button type="text" @click="deleteKey(valueInfo.key)">
                  <i class="el-icon-close" style="color: #F56C6C;" />
                </el-button>
              </el-tooltip>
            </el-col>
          </el-row>
          <el-divider></el-divider>
          <el-row class="server-value-val">
            <div style="height: 300px;">
              <el-scrollbar>
                <json-view v-if="valueInfo.value" :data="valueInfo" />
              </el-scrollbar>
            </div>
          </el-row>
        </div>
      </div>
      <div style="width: 35%; height: 100%;">
        <el-row>
          <el-col :span="12" style="font-weight: bold; padding-top: 10px;">Redis 服务器信息</el-col>
          <el-col :span="12" style="text-align: right; padding-right: 15px;">
            <el-button type="text" @click="getRedisCacheInfo">刷新</el-button>
          </el-col>
        </el-row>
        <el-divider>基本信息</el-divider>
        <el-row>
          <el-col :span="6" class="redis-info">Redis版本</el-col>
          <el-col :span="18" class="redis-info info-value">{{redisInfo.redis_version}}</el-col>
        </el-row>
        <el-row>
          <el-col :span="6" class="redis-info">已执行的命令</el-col>
          <el-col :span="18" class="redis-info info-value">{{redisInfo.total_commands_processed}}</el-col>
        </el-row>
        <el-row>
          <el-col :span="6" class="redis-info">已使用的内存</el-col>
          <el-col :span="18" class="redis-info info-value">{{redisInfo.used_memory_rss}}</el-col>
        </el-row>
        <el-row>
          <el-col :span="6" class="redis-info">正常运行时间</el-col>
          <el-col :span="18" class="redis-info info-value">{{redisInfo.uptime_in_seconds}}</el-col>
        </el-row>
        <el-divider>更多信息</el-divider>
        <div>
          <el-row>
            <el-col :span="6" class="redis-info">arch_bits</el-col>
            <el-col :span="18" class="redis-info info-value">{{redisInfo.arch_bits}}</el-col>
          </el-row>
          <el-row>
            <el-col :span="6" class="redis-info">config_file</el-col>
            <el-col :span="18" class="redis-info info-value">{{redisInfo.config_file}}</el-col>
          </el-row>
          <el-row>
            <el-col :span="6" class="redis-info">hz</el-col>
            <el-col :span="18" class="redis-info info-value">{{redisInfo.hz}}</el-col>
          </el-row>
          <el-row>
            <el-col :span="6" class="redis-info">lru_clock</el-col>
            <el-col :span="18" class="redis-info info-value">{{redisInfo.lru_clock}}</el-col>
          </el-row>
          <el-row>
            <el-col :span="6" class="redis-info">multiplexing_api</el-col>
            <el-col :span="18" class="redis-info info-value">{{redisInfo.multiplexing_api}}</el-col>
          </el-row>
          <el-row>
            <el-col :span="6" class="redis-info">os</el-col>
            <el-col :span="18" class="redis-info info-value">{{redisInfo.os}}</el-col>
          </el-row>
          <el-row>
            <el-col :span="6" class="redis-info">process_id</el-col>
            <el-col :span="18" class="redis-info info-value">{{redisInfo.process_id}}</el-col>
          </el-row>
          <el-row>
            <el-col :span="6" class="redis-info">redis_build_id</el-col>
            <el-col :span="18" class="redis-info info-value">{{redisInfo.redis_build_id}}</el-col>
          </el-row>
          <el-row>
            <el-col :span="6" class="redis-info">redis_git_drity</el-col>
            <el-col :span="18" class="redis-info info-value">{{redisInfo.redis_git_drity}}</el-col>
          </el-row>
          <el-row>
            <el-col :span="6" class="redis-info">redis_git_sha1</el-col>
            <el-col :span="18" class="redis-info info-value">{{redisInfo.redis_git_sha1}}</el-col>
          </el-row>
          <el-row>
            <el-col :span="6" class="redis-info">redis_mode</el-col>
            <el-col :span="18" class="redis-info info-value">{{redisInfo.redis_mode}}</el-col>
          </el-row>
          <el-row>
            <el-col :span="6" class="redis-info">redis_version</el-col>
            <el-col :span="18" class="redis-info info-value">{{redisInfo.redis_version}}</el-col>
          </el-row>
          <el-row>
            <el-col :span="6" class="redis-info">run_id</el-col>
            <el-col :span="18" class="redis-info info-value">{{redisInfo.run_id}}</el-col>
          </el-row>
          <el-row>
            <el-col :span="6" class="redis-info">tcp_port</el-col>
            <el-col :span="18" class="redis-info info-value">{{redisInfo.tcp_port}}</el-col>
          </el-row>
          <el-row>
            <el-col :span="6" class="redis-info">uptime_in_days</el-col>
            <el-col :span="18" class="redis-info info-value">{{redisInfo.uptime_in_days}}</el-col>
          </el-row>
          <el-row>
            <el-col :span="6" class="redis-info">uptime_in_seconds</el-col>
            <el-col :span="18" class="redis-info info-value">{{redisInfo.uptime_in_seconds}}</el-col>
          </el-row>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import jsonView from 'vue-json-views'
import splitPane from 'vue-splitpane'
import './RedisCache.scss'
const JWT_TOKEN = 'jwt_token::'
import { doPostQs, doPostJson, doGet } from '@/utils/utils'
import * as ResponseStatus from '@/utils/response-status'
export default {
  name: 'RedisCache',
  components: {
    jsonView,
    splitPane,
  },
  data() {
    return {
      searchPattern: '*',
      searchCount: 1,
      redisInfo: {
        aof_rewrite_scheduled: 'N/A',
        number_of_cached_scripts: 'N/A',
        mem_clients_slaves: 'N/A',
        keyspace_misses: 'N/A',
        mem_allocator: 'N/A',
        multiplexing_api: 'N/A',
        aof_last_write_status: 'N/A',
        used_memory_peak_perc: 'N/A',
        run_id: 'N/A',
        uptime_in_seconds: 'N/A',
        maxmemory: 'N/A',
        active_defrag_hits: 'N/A',
        atomicvar_api: 'N/A',
        total_system_memory_human: 'N/A',
        gcc_version: 'N/A',
        second_repl_offset: 'N/A',
        arch_bits: 'N/A',
        master_repl_offset: 'N/A',
        instantaneous_output_kbps: 'N/A',
        total_commands_processed: 'N/A',
        repl_backlog_size: 'N/A',
        sync_full: 'N/A',
        sync_partial_err: 'N/A',
        aof_last_cow_size: 'N/A',
        configured_hz: 'N/A',
        used_memory_startup: 'N/A',
        mem_clients_normal: 'N/A',
        used_cpu_user: 'N/A',
        aof_enabled: 'N/A',
        redis_mode: 'N/A',
        rdb_changes_since_last_save: 'N/A',
        used_memory_lua: 'N/A',
        redis_build_id: 'N/A',
        connected_slaves: 'N/A',
        expired_keys: 'N/A',
        used_memory: 'N/A',
        active_defrag_misses: 'N/A',
        active_defrag_key_misses: 'N/A',
        used_memory_rss: 'N/A',
        rdb_last_bgsave_status: 'N/A',
        process_id: 'N/A',
        rejected_connections: 'N/A',
        master_replid: 'N/A',
        used_memory_rss_human: 'N/A',
        used_memory_scripts: 'N/A',
        rdb_bgsave_in_progress: 'N/A',
        rdb_last_cow_size: 'N/A',
        cluster_enabled: 'N/A',
        tcp_port: 'N/A',
        os: 'N/A',
        mem_replication_backlog: 'N/A',
        aof_current_rewrite_time_sec: 'N/A',
        allocator_rss_ratio: 'N/A',
        used_memory_overhead: 'N/A',
        repl_backlog_histlen: 'N/A',
        slave_expires_tracked_keys: 'N/A',
        used_cpu_sys: 'N/A',
        role: 'N/A',
        used_memory_dataset_perc: 'N/A',
        expired_stale_perc: 'N/A',
        allocator_frag_ratio: 'N/A',
        client_recent_max_input_buffer: 'N/A',
        aof_last_bgrewrite_status: 'N/A',
        used_memory_peak_human: 'N/A',
        hz: 'N/A',
        used_memory_human: 'N/A',
        maxmemory_policy: 'N/A',
        rss_overhead_ratio: 'N/A',
        mem_fragmentation_ratio: 'N/A',
        rdb_last_bgsave_time_sec: 'N/A',
        allocator_allocated: 'N/A',
        used_memory_dataset: 'N/A',
        blocked_clients: 'N/A',
        used_cpu_sys_children: 'N/A',
        repl_backlog_active: 'N/A',
        used_cpu_user_children: 'N/A',
        sync_partial_ok: 'N/A',
        aof_rewrite_in_progress: 'N/A',
        redis_git_sha1: 'N/A',
        rdb_current_bgsave_time_sec: 'N/A',
        active_defrag_running: 'N/A',
        aof_last_rewrite_time_sec: 'N/A',
        master_replid2: 'N/A',
        config_file: 'N/A',
        lazyfree_pending_objects: 'N/A',
        used_memory_scripts_human: 'N/A',
        loading: 'N/A',
        allocator_rss_bytes: 'N/A',
        db3: 'N/A',
        pubsub_channels: 'N/A',
        db1: 'N/A',
        used_memory_lua_human: 'N/A',
        db0: 'N/A',
        active_defrag_key_hits: 'N/A',
        allocator_frag_bytes: 'N/A',
        migrate_cached_sockets: 'N/A',
        redis_git_dirty: 'N/A',
        total_system_memory: 'N/A',
        mem_aof_buffer: 'N/A',
        rss_overhead_bytes: 'N/A',
        client_recent_max_output_buffer: 'N/A',
        connected_clients: 'N/A',
        mem_fragmentation_bytes: 'N/A',
        lru_clock: 'N/A',
        pubsub_patterns: 'N/A',
        expired_time_cap_reached_count: 'N/A',
        repl_backlog_first_byte_offset: 'N/A',
        total_net_output_bytes: 'N/A',
        executable: 'N/A',
        evicted_keys: 'N/A',
        total_connections_received: 'N/A',
        allocator_active: 'N/A',
        mem_not_counted_for_evict: 'N/A',
        redis_version: 'N/A',
        total_net_input_bytes: 'N/A',
        latest_fork_usec: 'N/A',
        maxmemory_human: 'N/A',
        used_memory_peak: 'N/A',
        allocator_resident: 'N/A',
        keyspace_hits: 'N/A',
        uptime_in_days: 'N/A',
        instantaneous_input_kbps: 'N/A',
        instantaneous_ops_per_sec: 'N/A',
        rdb_last_save_time: 'N/A',
      },
      keysList: '',
      keysCount: 0,
      valueInfo: {
        key: '',
        value: '',
        size: '',
        expire: '',
        expireFormat: '',
      },
      valueInterval: null,
    }
  },
  computed: {},
  mounted() {
    this.getRedisCacheInfo()
    this.getKeysCount()
  },
  beforeDestroy() {
    clearInterval(this.valueInterval)
  },
  methods: {
    // 获取Redis缓存信息
    getRedisCacheInfo() {
      doGet('/redis-manage/info', {})
        .then((res) => {
          this.redisInfo = res.data
          this.redisInfo.uptime_in_days = this.redisInfo.uptime_in_days + '天'
          this.redisInfo.uptime_in_seconds = this.formaturDing(
            this.redisInfo.uptime_in_seconds
          )
          this.redisInfo.used_memory_rss =
            (this.redisInfo.used_memory_rss / 1024 / 1024).toFixed(2) + 'M'
        })
        .catch((err) => {})
    },
    // 获取key的个数
    getKeysCount() {
      doGet('/redis-manage/keys-count', {})
        .then((res) => {
          this.keysCount = res.data
        })
        .catch((err) => {})
    },
    // 查询key
    searchKeys() {
      if (this.searchCount === null) {
        this.searchCount = 1
      }
      doPostQs(
        '/redis-manage/keys',
        {
          count: this.searchCount,
          pattern: this.searchPattern,
        },
        {}
      )
        .then((res) => {
          this.getKeysCount()
          if (res.data.total === 0) {
            this.$message.warning('未获取到key')
            return
          }
          this.valueInfo = ''
          this.keysList = res.data.rows
        })
        .catch((err) => {})
    },
    // 根据key查询value的值信息
    getValueByKey(key) {
      if (key === '') {
        this.$message.warning('key为空')
        return
      }
      if (this.valueInterval != null) {
        clearInterval(this.valueInterval)
      }
      doPostQs(
        '/redis-manage/value',
        {
          key: key,
        },
        {}
      )
        .then((res) => {
          this.valueInfo = res.data
          if (this.valueInfo.expire !== -1) {
            let self = this
            let time = (this.valueInfo.expire / 1000).toFixed(0)
            this.valueInfo.expire = time + ' s'
            this.valueInfo.expireFormat = '（' + this.formaturDing(time) + '）'
            this.valueInterval = setInterval(() => {
              if (this.valueInfo) {
                time -= 1
                this.valueInfo.expire = time + ' s'
                this.valueInfo.expireFormat =
                  '（' + this.formaturDing(time) + '）'
              } else {
                clearInterval(this.valueInterval)
              }
            }, 1000)
          }
          this.valueInfo.size = this.valueInfo.size + ' byte'
        })
        .catch((err) => {})
    },
    // 删除key
    deleteKey(key) {
      if (key === '') {
        this.$message.error('key为空')
        return
      }
      if (key.indexOf(JWT_TOKEN) != -1) {
        this.$message.warning('该缓存为用户登录缓存，不可删除')
        return
      }
      doPostQs(
        '/redis-manage/del-key',
        {
          key: key,
        },
        {}
      )
        .then((res) => {
          this.$message.success(res.message)
          this.searchKeys()
        })
        .catch((err) => {})
    },
    // 清空缓存
    deleteKeys() {
      if (this.keysCount <= 0 || this.keysList.length <= 0) {
        this.$message.warning('未获取到缓存，不需要清空')
        return
      }
      let keys = []
      this.keysList.forEach((keyItem) => {
        if (keyItem.key.indexOf(JWT_TOKEN) == -1) {
          keys.push(keyItem.key)
        }
      })
      doPostJson('/redis-manage/del-keys', keys, {})
        .then((res) => {
          this.$message.success(res.message)
          this.searchKeys()
        })
        .catch((err) => {})
    },
    // 格式化时间
    formaturDing(mss) {
      let days = parseInt(mss / (60 * 60 * 24))
      let hours = parseInt((mss % (60 * 60 * 24)) / (60 * 60))
      let minutes = parseInt((mss % (60 * 60)) / 60)
      let seconds = mss % 60
      return (
        days + ' 天 ' + hours + ' 小时 ' + minutes + ' 分 ' + seconds + ' 秒 '
      )
    },
  },
}
</script>


